#include "stdafx.h"
#include "MouseTracerManagerImpl1.h"
#include "MouseTracer.h"
#include "MGCfg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

HHOOK CMouseTracerManagerImpl1::m_hHook = NULL;
CMouseTracerManagerImpl1::TRACERMAP CMouseTracerManagerImpl1::m_mouseTracers;

CMouseTracerManagerImpl1::CMouseTracerManagerImpl1()
{
}


CMouseTracerManagerImpl1::~CMouseTracerManagerImpl1()
{
	if (m_hHook)
	{
		::UnhookWindowsHookEx(m_hHook);
		m_hHook = NULL;
	}
}

/**
*	CMouseTracerManagerImpl1::Setup(HINSTANCE hMod)
*
*
*	@return BOOL
*
*/

BOOL CMouseTracerManagerImpl1::Setup(HINSTANCE hMod)
{
	if (m_hHook)
		return TRUE;
	
	if(hMod == NULL)
		hMod = (HINSTANCE)::GetModuleHandle(NULL);
	
	if (::EnumChildWindows(AfxGetMainWnd()->GetSafeHwnd(), SetupExistingWindowsProc, NULL))
	{
		// succeeded to install mouse tracers to all the exising windows
		m_hHook = ::SetWindowsHookEx(WH_CALLWNDPROCRET, CallWndRetProc, hMod, ::GetCurrentThreadId());
		
		// succeeded to install a window creation/destruction monitoring hook
		if (m_hHook)
			return TRUE;
	}
	
	// failed to setup, clean up immatured
	Remove();
	
	return FALSE;
}

void CMouseTracerManagerImpl1::TRACERMAP::UnhookAndDelete_t::UnhookAndDelete(HWND hWnd, LPVOID pPtr)
{
	CMouseTracer<CMouseTracerManager<CMouseTracerManagerImpl1> > * pHook = (CMouseTracer<CMouseTracerManager<CMouseTracerManagerImpl1> > *)pPtr;
	if(pHook && ::IsWindow(hWnd))
	{
		pHook->Unhook();
		delete pHook;
	}
}

/**
*	CMouseTracerManagerImpl1::Remove()
*
*
*	@return void
*
*/
void CMouseTracerManagerImpl1::Remove()
{
	if (m_hHook)
	{
		::UnhookWindowsHookEx(m_hHook);
		m_hHook = NULL;
	}
	
	std::for_each(m_mouseTracers.begin(), m_mouseTracers.end(), CMouseTracerManagerImpl1::TRACERMAP::UnhookAndDelete_t());
	m_mouseTracers.clear();
}

/**
*	CMouseTracerManagerImpl1::SetupExistingWindowsProc()
*
*	@param HWND hWnd
*	@param LPARAM lParam
*
*	@return BOOL CALLBACK
*
*/

BOOL CALLBACK CMouseTracerManagerImpl1::SetupExistingWindowsProc(HWND hWnd, LPARAM lParam)
{
	int nCatTarget;
	if ( (nCatTarget = MGCfg::Get().Action().CheckTargetValid(hWnd)) != -1)
	{
		TRACERMAP::iterator itMap = m_mouseTracers.find((LPVOID)hWnd);
		if(itMap == m_mouseTracers.end())
		{
			CMouseTracer<CMouseTracerManager<CMouseTracerManagerImpl1> > *pMouseTracer = new CMouseTracer<CMouseTracerManager<CMouseTracerManagerImpl1> >(TRUE);
			ASSERT(!::IsBadReadPtr(pMouseTracer, sizeof(CMouseTracer<CMouseTracerManager<CMouseTracerManagerImpl1> >)));
			pMouseTracer->Hook(hWnd);
			pMouseTracer->SetTargetCategory((BYTE)nCatTarget);
			m_mouseTracers[(LPVOID)hWnd] = (LPVOID)pMouseTracer;
		}
	}
	
	return TRUE;
}

/**
*	CMouseTracerManagerImpl1::CallWndRetProc()
*
*	@param int nCode
*	@param WPARAM wParam
*	@param LPARAM lParam
*
*	@return LRESULT CALLBACK
*
*/

LRESULT CALLBACK CMouseTracerManagerImpl1::CallWndRetProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	if(nCode < 0)
		return ::CallNextHookEx(m_hHook, nCode, wParam, lParam);
	
    if (nCode == HC_ACTION)
    {
        if (lParam)
        {
            CWPRETSTRUCT *pCWPR = reinterpret_cast<CWPRETSTRUCT *>(lParam);
			
			int nCatTarget;
            switch (pCWPR->message)                
            {
			case WM_CREATE:
				{
					if ((nCatTarget = MGCfg::Get().Action().CheckTargetValid(pCWPR->hwnd)) != -1)
					{
						TRACERMAP::iterator itMap = m_mouseTracers.find((LPVOID)pCWPR->hwnd);
						if(itMap == m_mouseTracers.end())
						{
							CMouseTracer<CMouseTracerManager<CMouseTracerManagerImpl1> > *pMouseTracer = new CMouseTracer<CMouseTracerManager<CMouseTracerManagerImpl1> >(TRUE);
							ASSERT(!::IsBadReadPtr(pMouseTracer, sizeof(CMouseTracer<CMouseTracerManager<CMouseTracerManagerImpl1> >)));
							pMouseTracer->Hook(pCWPR->hwnd);
							pMouseTracer->SetTargetCategory((BYTE)nCatTarget);
							m_mouseTracers[(LPVOID)pCWPR->hwnd] = (LPVOID)pMouseTracer;
						}
					}
				}
				break;
				
			case WM_DESTROY:
				{
					// don't delete mouse tracer here since it will be automatically deleted(removed) after the hooked window terminates
					// (when it receives WM_NCDESTROY message)
					TRACERMAP::iterator itMap = m_mouseTracers.find((LPVOID)pCWPR->hwnd);
					if(itMap != m_mouseTracers.end())
						m_mouseTracers.erase(itMap);
				}
				break;
            }
        }
    }
	
    return ::CallNextHookEx(m_hHook, nCode, wParam, lParam);
}
